/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2013/03/03
* File: VertexBuffer.cpp
* Blog: http://www.cnblogs.com/zengqh/
**/

#include "OGLVertexBuffer.h"
#include "OGL.h"

namespace HY
{
OGLVertexBuffer::~OGLVertexBuffer()
{
	release();
}

void OGLVertexBuffer::bind()
{
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle_);
}

void OGLVertexBuffer::unbind()
{
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

uint32 OGLVertexBuffer::getHandle() const
{
	return handle_;
}

OGLVertexBuffer::OGLVertexBuffer()
: handle_(0)
, usage_api_(0)
, lock_data_(0)
{
	memset(offset_, -1, sizeof(int32) * NumOfSemantics * MaxPipelineChannels);
}

bool OGLVertexBuffer::addVertexAttribute(Attribute& attr)
{
	switch (attr.type_)
	{
	case Float1:
		attr.size_api_ = sizeof(float);
		attr.type_api_ = GL_FLOAT;
		attr.components_api = 1;
		break;

	case Float2:
		attr.size_api_ = sizeof(float) * 2;
		attr.type_api_ = GL_FLOAT;
		attr.components_api = 2;
		break;

	case Float3:
		attr.size_api_ = sizeof(float) * 3;
		attr.type_api_ = GL_FLOAT;
		attr.components_api = 3;
		break;

	case Float4:
		attr.size_api_		  = sizeof(float) * 4;
		attr.type_api_		  = GL_FLOAT;
		attr.components_api = 4;
		break;

	case Short2:
		attr.size_api_		  = sizeof(short) * 2;
		attr.type_api_		  = GL_SHORT;
		attr.components_api = 2;
		break;

	case Short4:
		attr.size_api_		  = sizeof(short) * 4;
		attr.type_api_		  = GL_SHORT;
		attr.components_api = 4;
		break;
	default:
		HY_ASSERT(0);
		return false;
	}

	attr_list_.push_back(attr);

	attr_vertex_size += attr.size_api_;
	return true;
}


bool OGLVertexBuffer::create(uint32 elems, uint32 usage)
{
	if (usage == BufferUsage::Static)
	{
		usage_api_ = GL_STATIC_DRAW;
	}
	else if (usage == BufferUsage::Dynamic)
	{
		usage_api_ = GL_DYNAMIC_DRAW;
	}
	else if (usage == BufferUsage::WriteOnly)
	{
		usage_api_ = GL_STREAM_DRAW;
	}
	else
	{
		HY_ASSERT(0);
		return false;
	}

	elems = elems;
	size_ = elems * attr_vertex_size;
	usage_ = usage;

	glGenBuffers(1, &handle_);

	glBindBuffer(GL_ARRAY_BUFFER, handle_);
	glBufferData(GL_ARRAY_BUFFER, size_, 0, usage_api_);

	memset(offset_, -1, sizeof(int32) * NumOfSemantics * MaxPipelineChannels);

	for (uint32 i = 0; i < attr_list_.size(); ++i)
	{
		const Attribute &cAttribute = attr_list_[i];
		offset_[cAttribute.semantic_][cAttribute.channel_] = cAttribute.offset_;
	}

	return true;
}

void OGLVertexBuffer::release()
{
	if (!handle_)
	{
		return;
	}

	glDeleteBuffers(1, &handle_);
	handle_ = 0;

	elems_ = 0;
	usage_api_ = 0;
	size_ = 0;
	attr_list_.clear();

	memset(offset_, -1, sizeof(int32) * NumOfSemantics * MaxPipelineChannels);
}

void* OGLVertexBuffer::lock(uint32 flags)
{
	if (lock_data_)
	{
		return lock_data_;
	}

	uint32 nFlagAPI;
	if (flags == Lock::ReadOnly) 
	{
		nFlagAPI = GL_READ_ONLY;
	} 
	else if (flags == Lock::WriteOnly) 
	{
		nFlagAPI = GL_WRITE_ONLY;
	} 
	else if (flags == Lock::ReadWrite) 
	{
		nFlagAPI = GL_READ_WRITE;
	}
	else 
	{
		HY_ASSERT(0);
		return 0;
	}

	glBindBuffer(GL_ARRAY_BUFFER, handle_);
	lock_data_ = glMapBufferARB(GL_ARRAY_BUFFER, nFlagAPI);

	return lock_data_;
}

bool OGLVertexBuffer::unlock()
{
	if (!lock_data_)
	{
		return true;
	}

	if (!handle_)
	{
		HY_ASSERT(0);
		return false;
	}

	glBindBuffer(GL_ARRAY_BUFFER, handle_);
	glUnmapBuffer(GL_ARRAY_BUFFER);

	lock_data_ = 0;

	return true;
}

}